package master;
import java.awt.*;

import javax.swing.JFrame;
import javax.swing.JPanel;

import master.*;
import master.event.RegisterEvent;
import master.event.RegisterEventListener;
import master.userInterface.ComplexNumberPanel;
import master.userInterface.QubitPanel;

/**
 * A class for accessing a quantum register in terms of its individual quabits.
 * @author Luke Dobinson
 *
 */

public class Qubit extends ComplexNumber {
	final QuantumRegister myReg;
	final int myBit;
	public Qubit(QuantumRegister myReg, int myBit){
		super(0,0);
		if(myBit >= myReg.getQBitLength()||myBit <0)
			throw new IllegalArgumentException("Invalid Qubit");
		
		this.myReg = myReg;
		this.myBit = myBit;
	}
	public QuantumRegister getRegister(){
		return myReg;
	}
	public float getReal(){
		return getMagnitudes().getReal();
	}
	public float getImaginary(){
		return getMagnitudes().getImaginary();
	}
	
	/**
	 * return a complex number, the real part corresponding to the sum of the magnitude squared
	 * of all the eigenstates where this bit is a 1, and the complex where this bit is a zero
	 * @return
	 */
	public ComplexNumber getMagnitudes(){
		float oneMag = 0, zeroMag = 0;
		//real part corresponds to states with qubit = 1
		//imaginary corresponds to states with qubit = 0
		for(int i = 0 ; i < myReg.getDimension();i++){
			String bitString = myReg.bitString(i);
			if(bitString.charAt(myBit) == '1')
				oneMag += myReg.magSquared(i).getReal();
			else if(bitString.charAt(myBit) == '0')
				zeroMag += myReg.magSquared(i).getReal();
		}
		//System.out.println(Math.sqrt(oneMag)+Math.sqrt(zeroMag));
		return new ComplexNumber((float)Math.sqrt(oneMag),(float)Math.sqrt(zeroMag));
		
	}
	
	/**
	 * draw the qubit in network representation
	 */
	public void draw(Graphics g, int x , int y , float scale){
		g.drawLine(x , y + (int)(myBit*scale) , x + (int)scale , y + (int)(myBit*scale) );
	}
	
	/**
	 * returns the theta value for this qubit on bloch representation
	 * @return
	 */
	public float [] get3DTheta(){
		float [] theta = new float[myReg.dimension];
		for(int i = 0 ; i < myReg.dimension ; i++){
			String bitString = myReg.bitString(i);
			theta[i] = (float)Math.PI/2;
			if(bitString.charAt(myBit) == '0')
				theta[i] -= myReg.getCoefficient(i).magnitudeFloat();
			else if(bitString.charAt(myBit) == '1')
				theta[i] += myReg.getCoefficient(i).magnitudeFloat();
		}
		return theta;
	}
	
	/**
	 * returns the phi valu8e for this qubit in bloch representation
	 * @return
	 */
	public float [] get3DPhi(){
		float [] values = new float[myReg.dimension];
		for(int i = 0 ; i < myReg.dimension ; i++){
			values[i] = (float) myReg.getCoefficient(i).phaseFloat();
		}
		return values;
	}
	
	public float [] get3DR(){
		float [] values = new float[myReg.dimension];
		for(int i = 0 ; i < myReg.dimension ; i++){
			values[i] = (float) myReg.getCoefficient(i).magnitudeFloat();
		}
		return values;
	}
	
	/**
	 * display this qubit as A complex number
	 * @param title
	 * @param horizontal
	 * @param editable
	 * @param numbers
	 */
	public static void display(String title,boolean horizontal , boolean editable, ComplexNumber ... numbers){
		JFrame myFrame = new JFrame();
		myFrame.setTitle(title);
		int defaultDimension = 200;
		if(horizontal){
			myFrame.setLayout(new GridLayout(1,numbers.length));
			myFrame.setSize(defaultDimension*numbers.length,defaultDimension);
		}
		else{
			myFrame.setLayout(new GridLayout(numbers.length,1));
			myFrame.setSize(defaultDimension,defaultDimension*numbers.length);
		}
		for(int i = 0 ; i < numbers.length ; i++){
			myFrame.getContentPane().add(
					new ComplexNumberPanel(myFrame,numbers[i],75f,editable)
			);
		}
		
		myFrame.setVisible(true);
	}
	
	/**
	 * create a new panel displaying this qubit in "3D" bloch representation with default scaling
	 * @param defaultDimension
	 * @param horizontal
	 * @param editable
	 * @param numbers
	 * @return
	 */
	public static JPanel display(int defaultDimension,boolean horizontal , boolean editable, Qubit ... numbers){
		return display(100f, defaultDimension,  horizontal ,editable, numbers);
	}
	/**
	 * create a new panel displaying this qubit in "3D" bloch representation
	 * @param scale
	 * @param defaultDimension
	 * @param horizontal
	 * @param editable
	 * @param numbers
	 * @return
	 */
	public static JPanel display(float scale,int defaultDimension,boolean horizontal , boolean editable, Qubit ... numbers){
		JPanel mainPanel = new JPanel();
		if(horizontal){
			mainPanel.setLayout(new GridLayout(1,numbers.length));
			mainPanel.setSize(defaultDimension*numbers.length,defaultDimension);
		}
		else{
			mainPanel.setLayout(new GridLayout(numbers.length,1));
			mainPanel.setPreferredSize(new Dimension(defaultDimension,defaultDimension*numbers.length));
		}
		for(int i = 0 ; i < numbers.length ; i++){
			mainPanel.add(
					new QubitPanel(mainPanel,numbers[i],scale,editable)
			);
		}
		
		return mainPanel;
	}
	
	/**
	 * perform a classical measurement of the qubit
	 */
	public void measure(){
		myReg.measureQBit(myBit);
	}
	/**
	 * returns the total phase difference between differing states
	 * @return
	 */
	private float phaseDifference(){//returns the total phase difference between states
		float phOne=0,phZero=0;
		for(int i = 0; i < myReg.getDimension();i++){
			if(myReg.bitString(i).charAt(myBit)=='0'){
				phZero += myReg.getCoefficient(i).phase().getReal();
			}else
				phOne += myReg.getCoefficient(i).phase().getReal();
		}
		return phOne-phZero;
	}
	
}
